// 交叉类型  &可以理解为'与'
const mergeObj = <T, U>(a: T, b: U): T & U => {
  let res = {} as T & U //
  res = Object.assign(a, b)
  return res
}

// 联合类型
const showRes = (): string | number => {
  const arr = ['Samuel', 123]
  return Math.random() > 0.5 ? arr[0] : arr[1]
}

interface Bird {
  fly():void
  layEggs():void
}
interface Turtle {
  swim():void
  layEggs():void
}

function getSmallPet ():Bird|Turtle {
  return {} as Bird|Turtle
}

getSmallPet().layEggs() // 返回的联合类型只能访问其类型的共有属性
// getSmallPet().swim()  // 各自的非共有属性无法访问

// 类型保护
function isString (x: string | number): x is string {
  return typeof x === 'string'
}

const res1 = showRes()
if (isString(res1)) {
  console.log('string length:', res1.length)
} else {
  console.log('number:', res1.toFixed())
}

/*
typeof只能准确判断四种类型: string/number/boolean/symbol/undefined/function,其他都是object(null也是）
ts中可以用typeof直接做类型保护，但是仅限于判断string/number/boolean/symbol, 其他类型不会被识别为类型保护
 */

// 对于简单的类型保护，可以直接使用typeof(只能做===或!==判断)
if (typeof res1 === 'string') {
  console.log('string length:', res1.length)
} else {
  console.log('number:', res1.toFixed())
}

// instanceof 类型保护，判断是否是特定类创建的对象
class Axx {
  public age = 13
}

class Bxx {
  public name = 'Sam'
}

function getRandomAB () {
  return Math.random() > 0.5 ? new Axx() : new Bxx()
}

const rsAB = getRandomAB()
if (rsAB instanceof Axx) {
  console.log(rsAB.age)
} else {
  console.log(rsAB.name)
}

// null or undefined类型断言 !
function getPrefixStr (x:number|null) {
  function addPrefix (prefix:string):string {
    // 因为下面已经对null情况进行排除,这里使用!类型断言x不为null/undefined
    return prefix + x!.toFixed().toString()
  }
  x = x || 0.1
  return addPrefix('pr')
}

// 用type给抽象类取别名后,不能extends别名
abstract class AbsC {
  protected name:string
  protected constructor () {
    this.name = 'Samuel'
  }
}

type absc = AbsC // 不能Extends
class CCC extends AbsC {
  constructor () {
    super()
  }
}

const abscEntity:AbsC = new CCC()

// 字符串字面量类型
type Direction = 'east'|'west'|'south'|'north'
const DirFn = (direc:Direction) => {
  console.log(direc)
}
DirFn('west')
// DirFn('hollyshit') // Argument of type '"hollyshit"' is not assignable to parameter of type 'Direction'.

// 同理还有数字字面量类型

/*
 可辨识联合类型:
 1. 具有普通单例类型属性(说白了就是唯一的特征)
 2. 一个类型别名包含其他类型的联合
 */
interface Square {
  kind:'Square', // 这里kind就是唯一的特征
  width: number
}
interface Rectangle {
  kind:'Rectangle',
  width: number,
  height:number
}
interface Triangle {
  kind:'Triangle',
  width:number,
  height:number
}
type Shape = Square | Rectangle | Triangle
function getArea (s:Shape) {
  let area:number
  switch (s.kind) {
    case 'Rectangle':
      area = s.height * s.width
      break
    case 'Square':
      area = s.width * s.width
      break
    case 'Triangle':
      area = s.height * s.width / 2
  }
  return area
}
const sp:Triangle = {
  kind: 'Triangle',
  width: 3,
  height: 2
}
console.log(getArea(sp))
